<template>
  <code ref="oCode">console.log('hello world!');</code>
  <p>
    <button @click="modifyMultiple(true, false)">修改state.a</button> |
    <button @click="modifyMultiple(false, true)">修改count.value</button> |
    <button @click="modifyMultiple(true, true)">修改state.a和count.value</button>
  </p>
</template>

<script lang="ts">
import {
  defineComponent,
  reactive,
  ref,
  watchEffect,
  watch,
  onMounted,
} from "vue";
export default defineComponent({
  name: "c-api-watch",
  props: {
    point: String,
  },
  setup(props, context) {
    /**
     * reactive refs 和 template refs 的概念已统一.
     * @description 将 root 暴露在渲染上下文中，并通过 ref="root" 绑定到 div 作为其 ref。
     *  在 Virtual DOM patch 算法中，如果一个 VNode 的 ref 对应一个渲染上下文中的 ref，则
     *  该 VNode 对应的元素或组件实例将被分配给该 ref.value。 这是在 Virtual DOM 的
     *  mount / patch 过程中执行的，因此模板 ref 仅在渲染初始化后才能访问。
     * NOTICE(是否使用v-bind的写法不同): `ref="oCode"` 或者 `:ref="el => { oCode = el }"`
     */
    const oCode = ref(null);

    /**
     * @description 立即执行传入的一个函数，并响应式追踪其依赖，并在`初次运行和其依赖变更时`重新运行该函数。
     * @description 仅仅监听watchEffect的函数参数中使用的响应式数据
     * @return 1.在组件卸载时自动停止 2.调用返回值即可手动停止侦听
     */
    const stop1 = watchEffect((onInvalidate) => {
      console.log("API for Watch: watchEffect", props);
      onInvalidate(() => {
        // id 改变时 或 停止侦听时
        // 取消之前的异步操作
        console.log("#清除副作用");
      });
    });

    /**
     * @description 初始运行时同步执行副作用; 当一个用户定义的副作用函数进入队列时, 会在所有的组件更新后执行
     * @description 初始化运行是在组件 mounted 之前执行的(若希望在编写副作用函数时访问 DOM（或元素模板ref），请在onMounted钩子中进行)
     * @description debug调试
     * onTrack 和 onTrigger 选项可用于调试一个侦听器的行为。
     * 当一个 reactive 对象属性或一个 ref 作为依赖被追踪时，将调用 onTrack
     * 依赖项变更导致副作用被触发时，将调用 onTrigger
     * 这两个回调都将接收到一个包含有关所依赖项信息的调试器事件。建议在以下回调中编写 debugger 语句来检查依赖关系
     * onTrack 和 onTrigger 仅在开发模式下生效。
     */
    onMounted(() => {
      watchEffect(
        () => {
          console.log("----watchEffect在mounted内部可访问DOM----", oCode.value);
          // 在这里可以访问到 DOM 或者 template refs
        },
        {
          //  flush?: 'pre' | 'post' | 'sync',
          // onTrack?: (event: DebuggerEvent) => void,
          // onTrigger?: (event: DebuggerEvent) => void,
          /****************************/
          // 默认值
          flush: "post",
          // 副作用需要同步或在组件更新之前重新运行
          // flush: "sync",// 同步运行
          // flush: "pre",// 组件更新前执行
          onTrigger(e) {
            debugger;
          },
        }
      );
      // context.emit("change-second-level", props.point);
    });

    /**
     * watchEffect↑
     * watch↓: watch API 完全等效于 2.x this.$watch （以及 watch 中相应的选项）
     */

    const state = reactive({ a: 1 });
    const count = ref(0);
    /**
     * @description 侦听器的数据源可以是一个拥有`返回值的 getter 函数`，也可以是 ref: `[() => state.a, count]`
     * 即 直接监听reactive对象及其属性--拿不到前后变化的值
     * @description 懒执行副作用；
     * @description watch 和 watchEffect 在停止侦听, 清除副作用 (相应地 onInvalidate 会作为回调的第三个参数传
     * 入)，副作用刷新时机 和 侦听器调试 等方面行为一致.
     */
    watch(
      [state, () => state.a, count],
      ([_, a, count], [__, prevA, prevCount]) => {
        console.log("----watch: 支持单个或多个数据源----");
        console.log(state, state.a);
        console.log(a, prevA);
        console.log(count, prevCount);
      }
    );

    const modifyMultiple = (val1 = false, val2 = false) => {
      if (val1) {
        state.a++;
      }
      if (val2) {
        count.value++;
      }
    };
    return { oCode, modifyMultiple };
  },
});
</script>

<style lang="less">
</style>